#!/usr/bin/python
import error_messages
import sys


# global variable definitons for processor class:
# list of the program, needed for jump and store, load instructions 
program = []


# instruction set of the processing unit with the number of operands
instruction_set = {'START': 0, 'HALT': 0, 'IN': 2, 'OUT': 2, 'LDN': 2, 'ADD': 3, 'SUB': 3, 'MUL': 3, 'DIV': 3, 'ADDN': 3, 'SUBN': 3, 'MULN': 3, 'DIVN': 3, 'JMP': 1, 'JMPZ': 1, 'JMPP': 1, 'JMPN': 1, 'LOAD': 2, 'STOR': 2, 'PUSH': 1, 'POP': 1, 'JSR': 1, 'RET': 0}


# list of instructions 
instruction_list = ['START', 'HALT', 'IN', 'OUT', 'LDN', 'ADD', 'SUB', 'MUL', 'DIV', 'ADDN', 'SUBN', 'MULN', 'DIVN', 'JMP', 'JMPZ', 'JMPP', 'JMPN', 'LOAD', 'STOR', 'PUSH', 'POP', 'JSR', 'RET']


# initialize object for error messages
error = error_messages.error_messages()


# function to call exceptions 
def Exception_func(enter, *register):
        if type(enter) is not int: print error.output("no_int", type(enter)); sys.exit(1)
        if enter.bit_length() > 33: print error.output("bu_of_m", str(enter)); sys.exit(1)
        for i in register:
                if type(i) is not int: print error.output("no_int", type(i)); sys.exit(1)
                if not 0 <= i < 32: print error.output("bu_of_r"); sys.exit(1)


# I_START: initialize processor
def I_START(process, *operand):
	while (True):
		process.universal_register.append(0)
		if len(process.universal_register) > 31: return False
	process.program_counter_register = 0
	process.flag_register = 0

# I_HALT: stop processor
def I_HALT(process, *operand):
	print '***** End *****'	

# I_IN: read value from interface s and write to register rt
# s = operand[0]
# t = operand[1]
def I_IN(process, *operand):
	enter = raw_input('in <' + str(operand[0]) + '> ')
	if enter.strip("-").isdigit(): enter = int(enter) 
	else: print error.output("no_int", type(enter)); sys.exit(1)
	Exception_func(enter, *operand)
	process.universal_register[operand[1]] = enter

# I_OUT: load value from register rs to interface s
# rs = operand[0]
# s = operand[1]
def I_OUT(process, *operand):
	output = process.universal_register[operand[0]]
	Exception_func(output, *operand)
	print 'out <' + str(operand[1]) + '> ' + str(output)

# I_LDN: write constant k to register rt
# rt = operand[0]
def I_LDN(process, k, *operand):
	Exception_func(k, *operand)
	process.universal_register[operand[0]] = k

# I_ADD: add register rs2 to register rs1 and write result to register rt
# rs1 = operand[0]
# rs2 = operand[1]
# rt = operand[2]
def I_ADD(process, *operand):
	Exception_func(1, *operand)
	result = int(process.universal_register[operand[0]]) + int(process.universal_register[operand[1]])
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_SUB: subtract register rs2 from register rs1 and write result to register rt
# rs1 = operand[0]
# rs2 = operand[1]
# rt = operand[2]
def I_SUB(process, *operand):
	Exception_func(1, *operand)
	result = int(process.universal_register[operand[0]]) - int(process.universal_register[operand[1]])
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_MUL: multiply register rs1 by register rs2 and write result to register rt
# rs1 = operand[0]
# rs2 = operand[1]
# rt = operand[2]
def I_MUL(process, *operand):
	Exception_func(1, *operand)
	result = int(process.universal_register[operand[0]]) * int(process.universal_register[operand[1]])
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_DIV: divide register rs1 by register rs2 and write result to register rt
# rs1 = operand[0]
# rs2 = operand[1]
# rt = operand[2]
def I_DIV(process, *operand):
	Exception_func(1, *operand)
	if process.universal_register[operand[1]] != 0: result = int(process.universal_register[operand[0]]) / int(process.universal_register[operand[1]])
	else: error.output("div_0"); sys.exit(1)
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_ADDN: add constant k to register rs1 and write result to register rt
# rs1 = operand[0]
# k = operand[1]
# rt = operand[2]
def I_ADDN(process, *operand):
	Exception_func(1, *operand)
	result = int(process.universal_register[operand[0]]) + operand[1] 
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_SUBN: subtract constant k from register rs1 and write result to register rt
# rs1 = operand[0]
# k = operand[1]
# rt = operand[2]
def I_SUBN(process, *operand):
	Exception_func(1, *operand)
	result = int(process.universal_register[operand[0]]) - operand[1]
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_MULN: multiply register rs1 by constant k and write result to register rt
# rs1 = operand[0]
# k = operand[1]
# rt = operand[2]
def I_MULN(process, *operand):
	Exception_func(1, *operand)
	result = int(process.universal_register[operand[0]]) * operand[1]
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_DIVN: divide register rs1 by constant k and write result to register rt
# rs1 = operand[0]
# k = operand[1]
# rt = operand[2]
def I_DIVN(process, *operand):
	Exception_func(1, *operand)
	if operand[1] != 0: result = int(process.universal_register[operand[0]]) / operand[1]
	else: print error.output("div_0"); sys.exit(1)
	Exception_func(result, 1)
	process.universal_register[operand[2]] = result
	process.flag_register_value(process.universal_register[operand[2]])

# I_JMP: jump to program adress a
# a = operand[0]
def I_JMP(process, *operand):
	Exception_func(operand[0], 1)
	if not 0 <= operand[0] < len(program): error.output("in_pr_a"); sys.exit(1)
	process.program_counter_register = operand[0]

# I_JMPZ: jump to program adress a, if F=ZERO
# a = operand[0]
def I_JMPZ(process, *operand):
	Exception_func(operand[0], 1)
	if not 0 <= operand[0] < len(program): print error.output("in_pr_a"); sys.exit(1)
	if process.flag_register == 'ZERO': process.program_counter_register = operand[0]

# I_JMPP: jump to program adress, if F=POS
# a = operand[0]
def I_JMPP(process, *operand):
	Exception_func(operand[0], 1)
	if not 0 <= operand[0] < len(program): print error.output("in_pr_a"); sys.exit(1)
	if process.flag_register == 'POS': process.program_counter_register = operand[0]

# I_JMPN: jump to program adress a, if F=NEG
# a = operand[0]
def I_JMPN(process, *operand):
	Exception_func(operand[0], 1)
	if not 0 <= operand[0] <= len(program): print error.output("in_pr_a"); sys.exit(1)
	if process.flag_register == 'NEG': process.program_counter_register = operand[0]

# I_LOAD: load register ra implicit to rt
# ra = operand[0]
# rt = operand[1]
def I_LOAD(process, *operand):
	Exception_func(1, *operand)
	process.universal_register[operand[1]] = program[process.universal_register[operand[0]]]

# I_STOR: store register rs implicit in ra
# rs = operand[0]
# ra = operand[1]
def I_STOR(process, *operand): 
	Exception_func(1, *operand)
	program[process.universal_register[operand[1]]] = process.universal_register[operand[0]]
	
# I_PUSH: copy register value rs on stack
# rs = operand[0]
def I_PUSH(process, *operand):
	Exception_func(operand[0], 1)
	stack_pointer = process.universal_register[len(process.universal_register) - 1]
	stack_size = process.universal_register[len(process.universal_register) - 2] 
	if not 0 <= stack_pointer <= len(program): print error.output("in_st_a"); sys.exit(1)
	elif stack_pointer + stack_size <= len(program): print error.output("bu_of_r"); sys.exit(1)
	stack_pointer = stack_pointer - 1
	program[stack_pointer] = process.universal_register[operand[0]]
	process.universal_register[len(process.universal_register) - 1] = stack_pointer

# I_POP: load register value rt from stack
# rt = operand[0]
def I_POP(process, *operand):
	Exception_func(operand[0], 1)
	stack_pointer = process.universal_register[len(process.universal_register) - 1]
	if not 0 <= stack_pointer <= len(program): print error.output("in_st_a"); sys.exit(1)
	process.universal_register[operand[0]] = program[stack_pointer]
	stack_pointer = stack_pointer + 1
	process.universal_register[len(process.universal_register) - 1] = stack_pointer

# I_JSR: jump to subroutine at a
# a = operand[0]
def I_JSR(process, *operand):
	Exception_func(operand[0], 1)
	stack_pointer = process.universal_register[len(process.universal_register) - 1]
	stack_size = process.universal_register[len(process.universal_register) - 2] 
	if not 0 <= stack_pointer <= len(program): print error.output("in_st_a"); sys.exit(1)
	elif stack_pointer + stack_size <= len(program): print error.output("bu_of_r"); sys.exit(1)
	stack_pointer = stack_pointer - 1
	program[stack_pointer] = process.program_counter_register
	process.program_counter_register = operand[0]
	process.universal_register[len(process.universal_register) - 1] = stack_pointer

# I_RET: return from subroutine to mainroutine
def I_RET(process, *operand):
	Exception_func(operand[0], 1)
	stack_pointer = process.universal_register[len(process.universal_register) - 1]
	if not 0 <= stack_pointer <= len(program): print error.output("in_st_a"); sys.exit(1)
	process.program_counter_register = program[stack_pointer]
	stack_pointer = stack_pointer + 1
	process.universal_register[len(process.universal_register) - 1] = stack_pointer


#Allocation Instruction: Function:
instruction = {'START': I_START, 'HALT': I_HALT, 'IN': I_IN, 'OUT': I_OUT, 'LDN': I_LDN, 'ADD': I_ADD, 'SUB': I_SUB, 'MUL': I_MUL, 'DIV': I_DIV, 'ADDN': I_ADDN, 'SUBN': I_SUBN, 'MULN': I_MULN, 'DIVN': I_DIVN, 'JMP': I_JMP, 'JMPZ': I_JMPZ, 'JMPP': I_JMPP, 'JMPN': I_JMPN, 'LOAD': I_LOAD, 'STOR': I_STOR, 'PUSH': I_PUSH, 'POP': I_POP, 'JSR': I_JSR, 'RET': I_RET}
